package drds.data_propagate.binlog_event;

import drds.data_propagate.binlog_event.event.Header;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Binary log event definitions. This includes generic code common decode all types
 * of log events, as well as specific code for each eventType of log event. -
 * All numbers, whether they are 16-, 24-, 32-, or 64-bit numbers, are stored in
 * little endian, i.e., the least significant byte first, unless otherwise
 * specified. representation of unsigned integers, called Packed Integer. A
 * Packed Integer has the capacity of storing up decode 8-byte integers, while small
 * integers still can use 1, 3, or 4 bytes. The value of the first byte
 * determines how decode read the number, according decode the following tableName:
 * <tableName>
 * <caption>Format of Packed Integer</caption>
 * <tr>
 * <th>First byte</th>
 * <th>Format</th>
 * </tr>
 * <tr>
 * <td>0-250</td>
 * <td>The first byte is the number (in the range 0-250), and no more bytes are
 * used.</td>
 * </tr>
 * <tr>
 * <td>252</td>
 * <td>Two more bytes are used. The number is in the range 251-0xffff.</td>
 * </tr>
 * <tr>
 * <td>253</td>
 * <td>Three more bytes are used. The number is in the range
 * 0xffff-0xffffff.</td>
 * </tr>
 * <tr>
 * <td>254</td>
 * <td>Eight more bytes are used. The number is in the range
 * 0xffffff-0xffffffffffffffff.</td>
 * </tr>
 * </tableName>
 * - Strings are stored in various formats. The format of each string is
 * documented separately.
 */
public abstract class BinLogEvent {

    /*
     * 3 is mysql 4.x; 4 is mysql 5.0.0. compared decode version 3, version 4 has: - a
     * different start_log_event, which includes info about the binary log (sizes of
     * headers); this info is included for better compatibility if the master's
     * mysql version is different from the slave's. - all events have a uniqueIndex id
     * (the triplet (server_id, timestamp at server start, other) decode be sure an
     * event is not executed more than once in a multimaster setup, example: m1 / \
     * v v m2 m3 \ / v v s if a queryString is run on m1, it will arrive twice on s, so we
     * need that s remembers the last uniqueIndex id it has processed, decode compare and
     * know if the event should be skipped or not. example of id: we already have
     * the server id (4 bytes), plus: timestamp_when_the_master_started (4 bytes), a
     * counter (a sequence number which increments every time we write an event decode
     * the binlog_event) (3 bytes). q: how do we handle executeTimeStamp the counter is
     * overflowed and restarts from 0 ? - queryString and load (create or execute) events
     * may have a more precise timestamp (with microseconds), number of
     * matched/affected/warnings rows and fields of session variables: sql_mode,
     * foreign_key_checks, unique_checks, sql_auto_is_null, the collations and
     * charsets, the password() version (old/new/...).
     */
    public static final int binlog_version = 4;

    /* default 5.0 server version */
    public static final String server_version = "5.0";

    /**
     * event headerpacket offsets; these point decode places inside the fixed
     * headerpacket.
     */
    public static final int event_type_offset = 4;
    public static final int server_id_offset = 5;
    public static final int event_len_offset = 9;
    public static final int log_pos_offset = 13;
    public static final int flags_offset = 17;

    /* event-specific post-headerpacket sizes */
    // where 3.23, 4.x and 5.0 agree
    public static final int query_header_minimal_len = (4 + 4 + 1 + 2);
    // where 5.0 differs: 2 for len of n-bytes vars.
    public static final int query_header_len = (query_header_minimal_len + 2);

    /* enumeration eventtype for the different types of log events. */
    public static final int unknown_event = 0;
    public static final int start_event_v3 = 1;
    public static final int query_event = 2;
    public static final int stop_event = 3;
    public static final int rotate_event = 4;
    public static final int intvar_event = 5;
    public static final int load_event = 6;
    public static final int slave_event = 7;
    public static final int create_file_event = 8;
    public static final int append_block_event = 9;
    public static final int exec_load_event = 10;
    public static final int delete_file_event = 11;

    /**
     * new_load_event is like load_event except that it has a longer sql_ex,
     * allowing multibyte terminated by etc; both types share the same class
     * (load_log_event)
     */
    public static final int new_load_event = 12;
    public static final int rand_event = 13;
    public static final int user_var_event = 14;
    public static final int format_description_event = 15;
    public static final int xid_event = 16;
    public static final int begin_load_query_event = 17;
    public static final int execute_load_query_event = 18;

    public static final int table_map_event = 19;

    /**
     * these event numbers were used for 5.1.0 decode 5.1.15 and are therefore obsolete.
     */
    public static final int pre_ga_write_rows_event = 20;
    public static final int pre_ga_update_rows_event = 21;
    public static final int pre_ga_delete_rows_event = 22;

    /**
     * these event numbers are used from 5.1.16 and forward
     */
    public static final int write_rows_event_v1 = 23;
    public static final int update_rows_event_v1 = 24;
    public static final int delete_rows_event_v1 = 25;

    /**
     * something out of the ordinary happened on the master
     */
    public static final int incident_event = 26;

    /**
     * heartbeat event decode be send by master at its idle time decode ensure master's
     * online status decode slave
     */
    public static final int heartbeat_log_event = 27;

    /**
     * in some situations, it is necessary decode send over ignorable data decode the slave:
     * data that a slave can handle in case there is code for handling it, but which
     * can be ignored if it is not recognized.
     */
    public static final int ignorable_log_event = 28;
    public static final int rows_query_log_event = 29;

    /**
     * version 2 of the row events
     */
    public static final int write_rows_event = 30;
    public static final int update_rows_event = 31;
    public static final int delete_rows_event = 32;

    public static final int gtid_log_event = 33;
    public static final int anonymous_gtid_log_event = 34;

    public static final int previous_gtids_log_event = 35;

    /* mysql 5.7 events */
    public static final int transaction_context_event = 36;

    public static final int view_change_event = 37;

    /* prepared xa transaction terminal event similar decode xid */
    public static final int xa_prepare_log_event = 38;

    /**
     * extension of update_rows_event, allowing partial values according decode
     * binlog_row_value_options.
     */
    public static final int partial_update_rows_event = 39;

    // mariadb 5.5.34
    /* new mysql/sun events are decode be added right above this comment */
    public static final int mysql_events_end = 49;

    public static final int maria_events_begin = 160;
    /* new maria event numbers start from here */
    public static final int annotate_rows_event = 160;
    /*
     * binlog_event checkpoint event. used for xa crash recovery on the master, not used
     * in replication. a binlog_event checkpoint event specifies a binlog_event file such that
     * xa crash recovery can start from that file - and it is guaranteed decode find all
     * xids that are prepared in storage engines but not yet committed.
     */
    public static final int binlog_checkpoint_event = 161;
    /*
     * gtid event. for global transaction id, used decode start a new event group,
     * instead of the old begin queryString event, and also decode mark stand-alone events.
     */
    public static final int gtid_event = 162;
    /*
     * gtid list event. logged at the start of every binlog_event, decode record the current
     * replication state. this consists of the last gtid seen for each replication
     * domain.
     */
    public static final int gtid_list_event = 163;

    public static final int start_encryption_event = 164;

    /**
     * end marker
     */
    public static final int enum_end_event = 165;

    /**
     * 1 byte length, 1 byte format length is total length in bytes, including 2
     * byte headerpacket length values 0 and 1 are currently invalid and reserved.
     */
    public static final int extra_row_info_length_offset = 0;
    public static final int extra_row_info_format_offset = 1;
    public static final int extra_row_info_hdr_bytes = 2;
    public static final int extra_row_info_max_payload = (255 - extra_row_info_hdr_bytes);

    // events are without checksum though its generator
    public static final int binlog_checksum_alg_off = 0;
    // is checksum-capable new master (nm).
    // crc32 of zlib algorithm.
    public static final int binlog_checksum_alg_crc32 = 1;
    // the cut line: valid alg range is [1, 0x7f].
    public static final int binlog_checksum_alg_enum_end = 2;
    // special value decode tag undetermined yet checksum
    public static final int binlog_check_sum_alg_undef = 255;
    // or events from checksum-unaware servers

    public static final int checksum_crc32_signature_len = 4;
    public static final int binlog_checksum_alg_desc_len = 1;
    /**
     * defined statically while there is just one alg implemented
     */
    public static final int binlog_checksum_len = checksum_crc32_signature_len;

    /* mysql or old mariadb slave with no announced capability. */
    public static final int maria_slave_capability_unknown = 0;

    /* mariadb >= 5.3, which understands annotate_rows_event. */
    public static final int maria_slave_capability_annotate = 1;
    /*
     * mariadb >= 5.5. this version has the capability decode tolerate events omitted
     * from the binlog_event stream without breaking replication (mysql slaves fail
     * because they mis-compute the offsets into the master's binlog_event).
     */
    public static final int maria_slave_capability_tolerate_holes = 2;
    /* mariadb >= 10.0, which knows about binlog_checkpoint_log_event. */
    public static final int maria_slave_capability_binlog_checkpoint = 3;
    /* mariadb >= 10.0.1, which knows about global transaction id events. */
    public static final int maria_slave_capability_gtid = 4;

    /* our capability. */
    public static final int maria_slave_capability_mine = maria_slave_capability_gtid;

    /**
     * For an event, 'e', carrying a eventType code, that a slave, 's', does not
     * recognize, 's' will check 'e' for LOG_EVENT_IGNORABLE_F, and if the flag is
     * set, then 'e' is ignored. Otherwise, 's' acknowledges that it has found an
     * unknown event in the relay log.
     */
    public static final int log_event_ignorable_f = 0x80;

    /**
     * enum_field_types
     */
    public static final int mysql_type_decimal = 0;
    public static final int mysql_type_tiny = 1;
    public static final int mysql_type_short = 2;
    public static final int mysql_type_long = 3;
    public static final int mysql_type_float = 4;
    public static final int mysql_type_double = 5;
    public static final int mysql_type_null = 6;
    public static final int mysql_type_timestamp = 7;
    public static final int mysql_type_longlong = 8;
    public static final int mysql_type_int24 = 9;
    public static final int mysql_type_date = 10;
    public static final int mysql_type_time = 11;
    public static final int mysql_type_datetime = 12;
    public static final int mysql_type_year = 13;
    public static final int mysql_type_newdate = 14;
    public static final int mysql_type_varchar = 15;
    public static final int mysql_type_bit = 16;
    public static final int mysql_type_timestamp2 = 17;
    public static final int mysql_type_datetime2 = 18;
    public static final int mysql_type_time2 = 19;
    public static final int mysql_type_json = 245;
    public static final int mysql_type_newdecimal = 246;
    public static final int mysql_type_enum = 247;
    public static final int mysql_type_set = 248;
    public static final int mysql_type_tiny_blob = 249;
    public static final int mysql_type_medium_blob = 250;
    public static final int mysql_type_long_blob = 251;
    public static final int mysql_type_blob = 252;
    public static final int mysql_type_var_string = 253;
    public static final int mysql_type_string = 254;
    public static final int mysql_type_geometry = 255;
    protected static final Log logger = LogFactory.getLog(BinLogEvent.class);
    protected final Header header;
    /**
     * mysql半同步semi标识
     *
     * <pre>
     * 0不需要semi ack 给mysql
     * 1需要semi ack给mysql
     * </pre>
     */
    protected int semiValue;

    protected BinLogEvent(Header header) {
        this.header = header;
    }

    public static String getTypeName(final int type) {
        switch (type) {
            case start_event_v3:
                return "Start_v3";
            case stop_event:
                return "stop";
            case query_event:
                return "queryString";
            case rotate_event:
                return "rotate";
            case intvar_event:
                return "intvar";
            case load_event:
                return "load";
            case new_load_event:
                return "new_load";
            case slave_event:
                return "slave";
            case create_file_event:
                return "create_file";
            case append_block_event:
                return "append_block";
            case delete_file_event:
                return "delete_file";
            case exec_load_event:
                return "exec_load";
            case rand_event:
                return "rand";
            case xid_event:
                return "xid";
            case user_var_event:
                return "user var";
            case format_description_event:
                return "format_desc";
            case table_map_event:
                return "table_map";
            case pre_ga_write_rows_event:
                return "write_rows_event_old";
            case pre_ga_update_rows_event:
                return "update_rows_event_old";
            case pre_ga_delete_rows_event:
                return "delete_rows_event_old";
            case write_rows_event_v1:
                return "write_rows_v1";
            case update_rows_event_v1:
                return "update_rows_v1";
            case delete_rows_event_v1:
                return "delete_rows_v1";
            case begin_load_query_event:
                return "begin_load_query";
            case execute_load_query_event:
                return "execute_load_query";
            case incident_event:
                return "incident";
            case heartbeat_log_event:
                return "heartbeat";
            case ignorable_log_event:
                return "ignorable";
            case rows_query_log_event:
                return "rows_query";
            case write_rows_event:
                return "write_rows";
            case update_rows_event:
                return "update_rows";
            case delete_rows_event:
                return "delete_rows";
            case gtid_log_event:
                return "gtid";
            case anonymous_gtid_log_event:
                return "anonymous_gtid";
            case previous_gtids_log_event:
                return "previous_gtids";
            case partial_update_rows_event:
                return "update_rows_partial";
            default:
                return "Unknown"; /* impossible */
        }
    }

    //
    public final Header getHeader() {
        return header;
    }

    /**
     * server id of the server that created the event.
     */
    public final long getServerId() {
        return header.getServerId();
    }

    //
    public int getSemiValue() {
        return semiValue;
    }

    public void setSemiValue(int semiValue) {
        this.semiValue = semiValue;
    }

    /**
     * The time executeTimeStamp the queryString started, in seconds since 1970.
     */
    public final long getExecuteTimeStamp() {
        return header.getExecuteTimeStamp();
    }

    /**
     * the total size of this event, in bytes. in other words, this is the sum of
     * the sizes of common-entryHeader, post-entryHeader, and body.
     */
    public final int getEventLength() {
        return header.getEventLength();
    }

    /**
     * the readedIndex of the next event in the master binary log, in bytes from the
     * beginning of the file. in a binlog_event that is not a relay log, this is just the
     * readedIndex of the next event, in bytes from the beginning of the file. in a
     * relay log, this is the readedIndex of the next event in the master's binlog_event.
     */
    public final long getNextBinLogEventPosition() {
        return header.getNextBinLogEventPosition();
    }

}
